/**
 * Author: Nazo
 * License: Public Domain
 */
module nazo.serialize;

import std.traits;
/**
 * シリアライズします。
 * Bugs: ポインタ/連想配列/float/double/realなどに未対応
 * Bugs: Tからrvの長さが一意に決まるのでそれを使うべし。
 */
ubyte[] serialize(T)(T t){
  ubyte[] rv;
  static if(is(T==struct)){
    rv~=serialize(t.tupleof);
  }else static if(__traits(isIntegral,T)){
    rv=(cast(ubyte*)[t].ptr)[0..T.sizeof];
  }else static if(__traits(isStaticArray,T)){
    rv=cast(ubyte[])t;
  }else static if(is(T U:U[])){
    rv=serialize(t.length) ~ cast(ubyte[])t;
  }else{
    static assert(0,"unsupported type "~T.stringof);
  }
  return rv;
}

ubyte[] serialize(TL...)(TL t){
  ubyte[] rv;
  foreach(x;t)
    rv~=serialize(x);
  return rv;
}

template ReturnType(T){
  static if(__traits(isStaticArray,T) && is(T U:U[])){
    alias U[] ReturnType;
  }else{
    alias T ReturnType;
  }
}

/**
 * デシリアライズします。
 * Bugs: ポインタ/連想配列/float/double/realなどに未対応
 */
void deserialize(T)(ubyte[] t, ref T obj){
  ubyte* ptr = t.ptr;
  deserialize(ptr, obj);
}

void deserialize(dummy=int, TL...)(ubyte[] t, ref TL obj){ // XXX: workaround
  ubyte* ptr = t.ptr;
  foreach(i,T;TL)
    deserialize(ptr, obj[i]);
}

void deserialize(T)(ref ubyte* t, ref T obj){
  static if(is(T==struct)){
    deserialize(t, obj.tupleof);
  }else static if(__traits(isIntegral,T)){
    obj=*cast(T*)t;
    t+=T.sizeof;
  }else static if(__traits(isStaticArray,T)&&is(T U:U[])){
    obj=cast(U[])t[0..T.sizeof];
    t+=T.sizeof;
  }else static if(is(T U:U[])){
    size_t len;
    deserialize(t, len);
    obj= (cast(U*)t)[0..len];
    t+=len*U.sizeof;
  }else{
    static assert(0,"unsupported type "~T.stringof);
  }
}

void deserialize(TL...)(ref ubyte* t, ref TL obj){
  foreach(i,T;TL)
    deserialize(t, obj[i]);
}

//untested
ubyte[] multiserialize(TL...)(TL[] tl){
  ubyte[] rv;
  foreach(t; tl)
    rv ~= serialize(t);
  return rv;
}

ubyte[] multiserialize(T)(T tl) if(__traits(isAssociativeArray, T)){
  ubyte[] rv;
  foreach(key, val; tl)
    rv ~= serialize(key, val);
  return rv;
}

void multideserialize2(alias f)(ubyte[] t){
  ubyte* ptr = t.ptr;
  multideserialize2!(f)(ptr, ptr+t.length);
}

void multideserialize2(alias f)(ref ubyte* t, ubyte* e){
  for(;t < e;){
    ParameterTypeTuple!(f) param;
    deserialize(t, param);
    f(param);
  }
}

//untested
void multideserialize(TL...)(ref ubyte[] t, ref TL[] obj){
  ubyte* ptr = t.ptr;
  multideserialize(ptr, ptr+t.length, obj);
}

//untested
void multideserialize(TL...)(ref ubyte* t, ubyte* e, ref TL[] obj){
  for(;t < e;){
    TL param;
    deserialize(t, param);
    obj ~= param;
  }
}

void multideserialize(T)(ref ubyte[] t, ref T obj) if(__traits(isAssociativeArray, T)){
  ubyte* ptr = t.ptr;
  multideserialize(ptr, ptr+t.length, obj);
}

void multideserialize(T)(ref ubyte* t, ubyte* e, ref T obj) if(__traits(isAssociativeArray, T)){
  for(;t < e;){
    typeof(T.keys[0]) key;
    typeof(T.values[0]) val;
    deserialize(t, key, val);
    obj[key] = val;
  }
}

/+import std.stdio;

struct Test{
  ubyte a;
  uint b;
  ubyte[2] c;
  uint[] d;
  string[] e;
}

void main(){
  ubyte[] data;
  Test test=Test(2,10,2,[3,4],["1","2"]);
  writefln(test.tupleof);
  data=serialize(test);
  writefln(data);
  test=deserialize!(Test)(data);
  writefln(test.tupleof);
}+/
